package regexInput;

import java.util.ArrayList;

public class NFABuild {
	private String[] identifiers;
	private NFA finalTable;
	
	public NFABuild(String[] identifiers){
		this.identifiers = identifiers;
		//adds epsilon to our identifiers
		String[] tmp = new String[identifiers.length+1];
		for (int i = 0; i < identifiers.length;i++){
			tmp[i] = identifiers[i];
		}
		tmp[tmp.length-1] = "E";
		this.identifiers = tmp;
		finalTable = new NFA(identifiers);
	}
	
	public void setFinalTable(NFA newTable){
		finalTable = newTable;
	}
	
	public NFA buildNFA(String regEx){
		String s = "";
		NFA nfaTable = new NFA(identifiers);
		int i = 0;
		int firstPoint = 0;
		int lastPoint = 1;
		
		//a loop through all of the characters in the regular expression
		while(i < regEx.length()){
			s="";
			//find what the next character equals
			if (regEx.charAt(i) == '$'){
				//loop through to get the next identifer -> s
				do{
					s+=regEx.charAt(i);
					i++;
					if (i>=regEx.length())
						break;
				}while((regEx.charAt(i) != '$')&&(regEx.charAt(i) != '|')&&(regEx.charAt(i) != '*')&&(regEx.charAt(i) != '(')&&(regEx.charAt(i) != ')')&&(regEx.charAt(i) != '+'));
				//add a new NFA so the table
				nfaTable.addNFA$(nfaTable.create$(s));
				firstPoint++;
				lastPoint++;
			}
			if (i < regEx.length()){
				if (regEx.charAt(i) == '('){
					s="";
					i++;
					//recursively makes another NFA from the inner RegEx
					firstPoint = nfaTable.getTable().size();
					if (firstPoint == 0)
						firstPoint++;
					nfaTable.addNFA$(buildNFA(getInnerRegEx(regEx,i)));
					lastPoint = nfaTable.getTable().size();
					
					i += findIOffset(regEx,i)+1;
				}
			}
			if (i < regEx.length()){
				if (regEx.charAt(i) == '*'){
					
					nfaTable.addNFAStar(firstPoint,lastPoint);
					firstPoint = lastPoint;
					lastPoint++;
					i++;
				}
			}
			if (i < regEx.length()){
				if (regEx.charAt(i) == '+'){
					nfaTable.addNFAPlus(firstPoint, lastPoint);
					i++;
				}
			}
			if (i < regEx.length()){
				if (regEx.charAt(i) == '|'){
					boolean paran = false;
					String newString = "";
					i++;
					if(regEx.charAt(i) == '('){
						paran =true;
						i++;
						newString = getInnerRegEx(regEx,i);
						nfaTable.addNFAOr(buildNFA(newString),firstPoint);
						break;
						
					}
					else{
					do{
						newString+=regEx.charAt(i);
						i++;
						if (i>=regEx.length())
							break;
					}while((regEx.charAt(i) != '$')&&(regEx.charAt(i) != '|')&&(regEx.charAt(i) != '*')&&(regEx.charAt(i) != '(')&&(regEx.charAt(i) != ')'));
					}
					if (paran){
						i++;
						firstPoint = nfaTable.getTable().size()-1;
						nfaTable.addNFAOr(buildNFA(newString),firstPoint);
					}
					else
						nfaTable.addNFAOr(buildNFA(newString),firstPoint);
				}
			}
		}
		
		nfaTable.getFinalStates().add(nfaTable.getTable().size());
		for (int j = 0; j < nfaTable.getTable().size();j++){
			if (nfaTable.getTable().get(j).get(nfaTable.getTable().get(j).size()-1).get(0) == -1){
				if (!nfaTable.getTable().get(j).get(nfaTable.getTable().get(j).size()-1).contains(j+1)){
					nfaTable.getTable().get(j).get(nfaTable.getTable().get(j).size()-1).remove(0);
					nfaTable.getTable().get(j).get(nfaTable.getTable().get(j).size()-1).add(j+1);

				}
			}
			else
				if (!nfaTable.getTable().get(j).get(nfaTable.getTable().get(j).size()-1).contains(j+1)){
					nfaTable.getTable().get(j).get(nfaTable.getTable().get(j).size()-1).add(0,j+1);
				}
				
		}
			
		
		finalTable = nfaTable;
		return nfaTable;
	}
	
	public NFA addRegEx(String regEx){
		NFA returnNFA = finalTable.addNewNFA(buildNFA(regEx));
		returnNFA.getFinalStates().add(returnNFA.getTable().size());
		return returnNFA;
	}
	
	public String getEndingRegEx(String regEx,int startingPoint){
		String s = "";
		for (int i = startingPoint; i < regEx.length() ; i++){
			s+=regEx.charAt(i);
		}
		return s;
		
	}
	
	public String getInnerRegEx(String regEx,int startingPoint){
		int openCounter = 1;
		String newString = "";
		for (int i = startingPoint; i< regEx.length();i++){
			if (regEx.charAt(i) == '(')
				openCounter++;
			else if (regEx.charAt(i) == ')')
				openCounter--;
			if (openCounter == 0)
				return newString;
			newString += regEx.charAt(i); 
		}
		return "";	
	}
	public int findIOffset(String regEx,int startingPoint){
		int openCounter = 1;
		int counter = 0;
		for (int i = startingPoint; i< regEx.length();i++){
			if (regEx.charAt(i) == '(')
				openCounter++;
			else if (regEx.charAt(i) == ')')
				openCounter--;
			if (openCounter == 0)
				return counter;
			counter++;
		}
		return counter;	
	}
	
	public NFA getFinalTable(){
		return finalTable;
	}
	
	public DSTable toDSTable(){
		DSTable newTable = new DSTable();
		for (String s : identifiers){
			newTable.addCharClasses(s);
		}
		ArrayList<ArrayList<ArrayList<String>>> tab = finalTable.tableToStringArray();
		// # of states
		for (int i = 0;i<tab.size();i++){
			
			TableNode newState = new TableNode(0);
			// # of identifiers
			for (int j = 0; j<tab.get(i).size();j++){
				// # of transitions per identifier
				newState.setTransitions(tab.get(i));
			}
			newTable.addSetState(newState);
		}
		return newTable;
	}
	
}
